﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Application.Services.Dto;
using Abp.Collections.Extensions;
using Abp.Runtime.Caching;
using DevM.Development.DataMigration.Dto;
using DevM.Development.Resources;
using DevM.Development.Resources.Dto;
using DevM.Sessions;
using Microsoft.AspNetCore.Http;

namespace DevM.Development.DataMigration
{
    public class DataMigrationAppService : DevMAppServiceBase, IDataMigrationAppService
    {

        private readonly ISessionAppService sessionAppService;
        private readonly IHttpContextAccessor httpContextAccessor;
        private readonly DataMigrationManager dataMigrationManager;
        private readonly ICacheManager cacheManager;
        private readonly ResourceManager resourceManager;

        public DataMigrationAppService(ISessionAppService sessionAppService, IHttpContextAccessor httpContextAccessor, DataMigrationManager dataMigrationManager, ICacheManager cacheManager, ResourceManager resourceManager)
        {
            this.sessionAppService = sessionAppService;
            this.httpContextAccessor = httpContextAccessor;
            this.dataMigrationManager = dataMigrationManager;
            this.cacheManager = cacheManager;
            this.resourceManager = resourceManager;
        }

        public async Task<ConfigDto> AddConfig(AddConfigInput input)
        {
            var config = ObjectMapper.Map<Config>(input);
            var currentUserInfomation = await sessionAppService.GetCurrentLoginInformations();
            config.CreationTime = DateTime.Now;
            config.CreatorUserId = currentUserInfomation.User?.Id ?? 0;
            config.TenantId = currentUserInfomation.Tenant?.Id;
            config = await dataMigrationManager.AddConfig(config);

            var configResources = input.ResourceIds.Select(r => new RecordResource
            {
                CreationTime = DateTime.Now,
                CreatorUserId = config.CreatorUserId,
                TenantId = config.TenantId,
                ResourceId = r,
                RecoordId = config.Id,
                RecoordType = typeof(Config).FullName
            }).ToList();
            await resourceManager.AddRecordResource(configResources);

            return ObjectMapper.Map<ConfigDto>(config);
        }

        public async Task<DataDto> AddData(AddDataInput input)
        {
            var data = ObjectMapper.Map<Data>(input);
            var currentUserInfomation = await sessionAppService.GetCurrentLoginInformations();
            data.CreationTime = DateTime.Now;
            data.CreatorUserId = currentUserInfomation.User?.Id ?? 0;
            data.TenantId = currentUserInfomation.Tenant?.Id;
            data = await dataMigrationManager.AddData(data);

            var dataResources = input.ResourceIds.Select(r => new RecordResource
            {
                CreationTime = DateTime.Now,
                CreatorUserId = data.CreatorUserId,
                TenantId = data.TenantId,
                ResourceId = r,
                RecoordId = data.Id,
                RecoordType = typeof(Data).FullName
            }).ToList();
            await resourceManager.AddRecordResource(dataResources);

            return ObjectMapper.Map<DataDto>(data);
        }

        public async Task<DatabaseDto> AddDatabase(AddDatabaseInput input)
        {
            var database = ObjectMapper.Map<Database>(input);
            var currentUserInfomation = await sessionAppService.GetCurrentLoginInformations();
            database.CreationTime = DateTime.Now;
            database.CreatorUserId = currentUserInfomation.User?.Id ?? 0;
            database.TenantId = currentUserInfomation.Tenant?.Id;
            database = await dataMigrationManager.AddDatabase(database);

            var databaseResources = input.ResourceIds.Select(r => new RecordResource
            {
                CreationTime = DateTime.Now,
                CreatorUserId = database.CreatorUserId,
                TenantId = database.TenantId,
                ResourceId = r,
                RecoordId = database.Id,
                RecoordType = typeof(Database).FullName
            }).ToList();
            await resourceManager.AddRecordResource(databaseResources);

            return ObjectMapper.Map<DatabaseDto>(database);
        }

        public async Task DeleteConfig(EntityDto<long> configId)
        {
            await dataMigrationManager.DeleteConfig(configId.Id);
            var configResources = resourceManager.RecordResources.Where(rr => rr.RecoordId == configId.Id && rr.RecoordType == typeof(Config).FullName);
            await resourceManager.DeleteRecordResource(typeof(Config).FullName, configResources.Select(fr => fr.Id).ToArray());
        }

        public async Task DeleteData(EntityDto<long> dataId)
        {
            await dataMigrationManager.DeleteData(dataId.Id);
            var dataResources = resourceManager.RecordResources.Where(rr => rr.RecoordId == dataId.Id && rr.RecoordType == typeof(Data).FullName);
            await resourceManager.DeleteRecordResource(typeof(Data).FullName, dataResources.Select(fr => fr.Id).ToArray());
        }

        public async Task DeleteDatabase(EntityDto<long> databaseId)
        {
            await dataMigrationManager.DeleteDatabase(databaseId.Id);
            var databaseResources = resourceManager.RecordResources.Where(rr => rr.RecoordId == databaseId.Id && rr.RecoordType == typeof(Database).FullName);
            await resourceManager.DeleteRecordResource(typeof(Database).FullName, databaseResources.Select(fr => fr.Id).ToArray());
        }

        public async Task<ConfigDto> GetConfig(EntityDto<long> configId)
        {
            var config = ObjectMapper.Map<ConfigDto>(dataMigrationManager.Configs.Where(c => c.Id == configId.Id).FirstOrDefault());
            var configResources = await resourceManager.GetRecordResources(typeof(Config).FullName, configId.Id);
            var resourcesDto = configResources.Select(rr => new ConfigResourceDto { Id = rr.Id, ConfigId = rr.RecoordId, ResourceId = rr.ResourceId }).ToList();
            resourcesDto.ForEach(async r =>
            {
                r.Resource = ObjectMapper.Map<ResourceDto>((await resourceManager.GetResourceListById(r.ResourceId)).FirstOrDefault());
            });

            config.Resources = resourcesDto;
            return config;
        }

        public async Task<PageOutput<ConfigDto>> GetConfigList(GetConfigListInput input)
        {
            var dataset = dataMigrationManager.Configs.WhereIf(input.ReleaseRecordId > 0, f => f.ReleaseRecordId == input.ReleaseRecordId).OrderByDescending(f => f.Id);
            var items = dataset.Skip(input.SkipCount).Take(input.MaxResultCount).ToList();
            var total = dataset.Count();
            return new PageOutput<ConfigDto>
            {
                Items = ObjectMapper.Map<List<ConfigDto>>(items),
                TotalCount = total
            };
        }

        public async Task<DataDto> GetData(EntityDto<long> dataId)
        {
            var data= ObjectMapper.Map<DataDto>(dataMigrationManager.Datas.Where(c => c.Id == dataId.Id).FirstOrDefault());
            var dataResources = await resourceManager.GetRecordResources(typeof(Data).FullName, dataId.Id);
            var resourcesDto = dataResources.Select(rr => new DataResourceDto { Id = rr.Id, DataId = rr.RecoordId, ResourceId = rr.ResourceId }).ToList();
            resourcesDto.ForEach(async r =>
            {
                r.Resource = ObjectMapper.Map<ResourceDto>((await resourceManager.GetResourceListById(r.ResourceId)).FirstOrDefault());
            });

            data.Resources = resourcesDto;
            return data;
        }

        public async Task<DatabaseDto> GetDatabase(EntityDto<long> databaseId)
        {
            var database= ObjectMapper.Map<DatabaseDto>(dataMigrationManager.Databases.Where(c => c.Id == databaseId.Id).FirstOrDefault());

            var databaseResources = await resourceManager.GetRecordResources(typeof(Database).FullName, databaseId.Id);
            var resourcesDto = databaseResources.Select(rr => new DatabaseResourceDto { Id = rr.Id, DatabaseId = rr.RecoordId, ResourceId = rr.ResourceId }).ToList();
            resourcesDto.ForEach(async r =>
            {
                r.Resource = ObjectMapper.Map<ResourceDto>((await resourceManager.GetResourceListById(r.ResourceId)).FirstOrDefault());
            });

            database.Resources = resourcesDto;
            return database;
        }

        public async Task<PageOutput<DatabaseDto>> GetDatabaseList(GetDatabaseListInput input)
        {
            var dataset = dataMigrationManager.Databases.WhereIf(input.ReleaseRecordId > 0, f => f.ReleaseRecordId == input.ReleaseRecordId).OrderByDescending(f => f.Id);
            var items = dataset.Skip(input.SkipCount).Take(input.MaxResultCount).ToList();
            var total = dataset.Count();
            return new PageOutput<DatabaseDto>
            {
                Items = ObjectMapper.Map<List<DatabaseDto>>(items),
                TotalCount = total
            };
        }

        public async Task<PageOutput<DataDto>> GetDataList(GetDataListInput input)
        {
            var dataset = dataMigrationManager.Datas.WhereIf(input.ReleaseRecordId > 0, f => f.ReleaseRecordId == input.ReleaseRecordId).OrderByDescending(f => f.Id);
            var items = dataset.Skip(input.SkipCount).Take(input.MaxResultCount).ToList();
            var total = dataset.Count();
            return new PageOutput<DataDto>
            {
                Items = ObjectMapper.Map<List<DataDto>>(items),
                TotalCount = total
            };
        }

        public async Task<ConfigDto> UpdateConfig(UpdateConfigInput input)
        {
            var config = ObjectMapper.Map<Config>(input);
            var currentUserInfomation = await sessionAppService.GetCurrentLoginInformations();
            config.LastModificationTime = DateTime.Now;
            config.LastModifierUserId = currentUserInfomation.User?.Id ?? 0;
            config.TenantId = currentUserInfomation.Tenant?.Id;

            var configResources = input.ResourceIds.Select(r => new RecordResource
            {
                CreationTime = DateTime.Now,
                CreatorUserId = config.CreatorUserId,
                TenantId = config.TenantId,
                ResourceId = r,
                RecoordId = config.Id,
                RecoordType = typeof(Config).FullName
            }).ToList();
            await resourceManager.AddRecordResource(configResources);

            return ObjectMapper.Map<ConfigDto>(await dataMigrationManager.UpdateConfig(config));
        }

        public async Task<DataDto> UpdateData(UpdateDataInput input)
        {
            var data = ObjectMapper.Map<Data>(input);
            var currentUserInfomation = await sessionAppService.GetCurrentLoginInformations();
            data.LastModificationTime = DateTime.Now;
            data.LastModifierUserId = currentUserInfomation.User?.Id ?? 0;
            data.TenantId = currentUserInfomation.Tenant?.Id;

            var dataResources = input.ResourceIds.Select(r => new RecordResource
            {
                CreationTime = DateTime.Now,
                CreatorUserId = data.CreatorUserId,
                TenantId = data.TenantId,
                ResourceId = r,
                RecoordId = data.Id,
                RecoordType = typeof(Data).FullName
            }).ToList();
            await resourceManager.AddRecordResource(dataResources);

            return ObjectMapper.Map<DataDto>(await dataMigrationManager.UpdateData(data));
        }

        public async Task<DatabaseDto> UpdateDatabase(UpdateDatabaseInput input)
        {
            var database = ObjectMapper.Map<Database>(input);
            var currentUserInfomation = await sessionAppService.GetCurrentLoginInformations();
            database.LastModificationTime = DateTime.Now;
            database.LastModifierUserId = currentUserInfomation.User?.Id ?? 0;
            database.TenantId = currentUserInfomation.Tenant?.Id;

            var databaseResources = input.ResourceIds.Select(r => new RecordResource
            {
                CreationTime = DateTime.Now,
                CreatorUserId = database.CreatorUserId,
                TenantId = database.TenantId,
                ResourceId = r,
                RecoordId = database.Id,
                RecoordType = typeof(Database).FullName
            }).ToList();
            await resourceManager.AddRecordResource(databaseResources);

            return ObjectMapper.Map<DatabaseDto>(await dataMigrationManager.UpdateDatabase(database));
        }

        public async Task<List<ResourceDto>> GetConfigResourcesByConfigId(EntityDto<long> configId)
        {
            var configResources = await resourceManager.GetRecordResources(typeof(Config).FullName, configId.Id);
            return ObjectMapper.Map<List<ResourceDto>>(await resourceManager.GetResourceListById(configResources.Select(fr => fr.ResourceId).ToArray()));
        }

        public async Task DeleteConfigResource(EntityDto<long> configId)
        {
            await resourceManager.DeleteRecordResource(typeof(Config).FullName, configId.Id);
        }

        public async Task<List<ResourceDto>> GetDataResourcesByDataId(EntityDto<long> dataId)
        {
            var dataResources = await resourceManager.GetRecordResources(typeof(Data).FullName, dataId.Id);
            return ObjectMapper.Map<List<ResourceDto>>(await resourceManager.GetResourceListById(dataResources.Select(fr => fr.ResourceId).ToArray()));
        }

        public async Task DeleteDataResource(EntityDto<long> dataId)
        {
            await resourceManager.DeleteRecordResource(typeof(Data).FullName, dataId.Id);
        }

        public async Task<List<ResourceDto>> GetDatabaseResourcesByDatabaseId(EntityDto<long> databaseId)
        {
            var databaseResources = await resourceManager.GetRecordResources(typeof(Database).FullName, databaseId.Id);
            return ObjectMapper.Map<List<ResourceDto>>(await resourceManager.GetResourceListById(databaseResources.Select(fr => fr.ResourceId).ToArray()));
        }

        public async Task DeleteDatabaseResource(EntityDto<long> databaseId)
        {
            await resourceManager.DeleteRecordResource(typeof(Database).FullName, databaseId.Id);

        }

    }
}
